package worms;

import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import network.*;
import network.messages.*;
import gameengine.*;

/**
 * Classe principal do jogo
 * @author Victor
 */
public class Jogo extends JFrame implements NetworkListener
{
	public static final int WIDTH_JOGO = 1024;
	public static  final int HEIGHT_JOGO = 720;
	private final int POSICAO_X_ESQUERDA = 116;
	private final int POSICAO_X_DIREITA = 884;
	private final int POSICAO_Y = 290;

	private Cliente cliente;
	
	private GameContainer _container;
	private SpriteEstatica background;
	private PlayerAtual eu;
	private Vector<Player> players = new Vector<Player>();

	private BarraLife barraP1;
	private BarraLife barraP2;
	private Espinho espinho;
	private Plataforma plataforma1;
	private Plataforma plataforma2;

	public Jogo(String ip, int porta)
	{
		super("Worms");

		_container = new GameContainer();
		_container.setVisible(true);
		_container.setSize(WIDTH_JOGO, HEIGHT_JOGO);
		PhysicsManager.SetDimension(_container.getWidth(), _container.getHeight());

		setLayout(null);
		setSize(WIDTH_JOGO, HEIGHT_JOGO);
		add(_container);

		setVisible(true);

		cliente = new Cliente();
		cliente.AddListener(this);
		cliente.Connect(ip, porta);
	}

	/**
	 * Retorna o cliente utilizado para comunicação de socket
	 * @owner Victor
	 */
	public Cliente GetCliente()
	{
		return cliente;
	}

	/**
	 * Retorna o game container
	 * @owner Victor
	 */
	public GameContainer GetContainer()
	{
		return _container;
	}

	/**
	 * Fecha o servidor e a janela
	 * @owner Victor
	 */
	private void Close()
	{
		MensagemLogout mensagemLogout = new MensagemLogout();
		mensagemLogout.SetFrom(cliente.GetId());
		cliente.SendContent(mensagemLogout.GetJSONString());

		cliente.Disconnect();
		_container.dispose();
		setVisible(false);

		dispose();
	}

	/**
	 * Classe que interpreta o teclado
	 * @owner Victor
	 */
	public class Teclado extends KeyAdapter
	{
		@Override
		public void keyPressed(KeyEvent e)
		{
			eu.TestarInput(e.getKeyCode());
		}

		@Override
		public void keyReleased(KeyEvent e)
		{
			eu.OnTeclaSolta(e.getKeyCode());
		}
	}

	/**
	 * Classe que ouve a movimentação do mouse
	 * @owner Victor
	 */
	public class MouseMotion extends MouseMotionAdapter
	{
		@Override
		public void mouseMoved(MouseEvent e)
		{
			eu.OnMouseMove(e);
		}
	}

	/**
	 * Classe que ouve os inputs do mouse
	 * @owner Victor
	 */
	public class Mouse extends MouseAdapter
	{
		@Override
		public void mouseClicked(MouseEvent e)
		{
			eu.OnMouseDown(e);
		}
	}

	/**
	 * Inicia o jogo, colocando a sprite do jogador e adicionando o fundo, deve ser chamado após o jogador desse cliente ter recebido um id
	 * @owner Victor
	 */
	private void StartGame()
	{
		background = new SpriteEstatica(new Textura("./src/worms/images/background.png"), new Point(0, 0));
		_container.AddSprite(background);

		SpriteEstatica arma1 = new SpriteEstatica(new Textura("./src/worms/images/arma.png"), new Point(61, 68));
		SpriteEstatica arma2 = new SpriteEstatica(new Textura("./src/worms/images/arma.png"), new Point(839, 68));
		_container.AddSprite(arma1);
		_container.AddSprite(arma2);

		espinho = new Espinho(_container);

		plataforma1 = new Plataforma(Player.LADO_ESQUERDO, _container);
		plataforma2 = new Plataforma(Player.LADO_DIREITO, _container);

		int posicaoXPlayer = cliente.GetId() % 2 == 0 ? POSICAO_X_ESQUERDA : POSICAO_X_DIREITA;
		int posicaoYPlayer = POSICAO_Y;

		eu = PlayerAtual.Create(cliente.GetId(), new Point(posicaoXPlayer, posicaoYPlayer), cliente, GetContainer());
		eu.SetAncora(new Point2D.Double(0, 0));
		eu.SetLado(cliente.GetId() % 2 != 0 ? Player.LADO_DIREITO : Player.LADO_ESQUERDO);

		barraP1 = new BarraLife(_container, eu);

		_container.AddSprite(eu);

		MensagemEnterPlayer enterPlayer = new MensagemEnterPlayer();
		enterPlayer.SetId(cliente.GetId());
		enterPlayer.SetPosicaoX(posicaoXPlayer);
		enterPlayer.SetPosicaoY(posicaoYPlayer);
		cliente.SendContent(enterPlayer.GetJSONString());

		addKeyListener(new Teclado());
		addMouseListener(new Mouse());
		addMouseMotionListener(new MouseMotion());

		addWindowListener(new java.awt.event.WindowAdapter()
		{
			@Override
			public void windowClosing(java.awt.event.WindowEvent evt)
			{
				formWindowClosing(evt);
			}
		});
	}

	/**
	 * Cria um adversário
	 * @owner Victor
	 */
	private void CriarAdversario(int id, Point posicao)
	{
		Player newPlayer = Player.CreateInimigo(id, posicao, GetContainer());
		newPlayer.SetAncora(new Point2D.Double(0.5, 0.5));
		newPlayer.SetLado(id % 2 != 0 ? Player.LADO_DIREITO : Player.LADO_ESQUERDO);
		players.add(newPlayer);
		_container.AddSprite(newPlayer);

		barraP2 = new BarraLife(_container, newPlayer);
	}

	/**
	 * Remove um adversário
	 * @owner Victor
	 */
	private void RemoverAdversario(int id)
	{
		Player playerToRemove = null;

		for (Player player : players)
		{
			if (player.GetId() == id)
			{
				playerToRemove = player;
				break;
			}
		}

		if (playerToRemove != null)
		{
			players.remove(playerToRemove);
			_container.RemoveSprite(playerToRemove);
		}
	}

	/**
	 * Disparado quando uma mensagem chega
	 * @owner Victor
	 */
	public void OnMensagemChegou(Mensagem mensagem)
	{
		if (mensagem instanceof MensagemAssignId)
		{
			StartGame();
		}
		else if (mensagem instanceof MensagemUpdatePosicaoPlayer)
		{
			MensagemUpdatePosicaoPlayer updatePosicaoPlayer = (MensagemUpdatePosicaoPlayer) mensagem;
			boolean achouPlayer = false;
			for (Player player : players)
			{
				if (player.GetId() == updatePosicaoPlayer.GetFrom())
				{
					//Atualiza a posição de um jogador que ja existe nessa instância
					player.SetPosicao(updatePosicaoPlayer.GetPosicaoX(), updatePosicaoPlayer.GetPosicaoY());
					achouPlayer = true;
					break;
				}
			}

			if (!achouPlayer)
			{
				//Caso alguma posição de um jogador que não exista nessa instância seja reportada, cria um jogador
				CriarAdversario(updatePosicaoPlayer.GetFrom(), new Point(updatePosicaoPlayer.GetPosicaoX(), updatePosicaoPlayer.GetPosicaoY()));
			}
		}
		else if (mensagem instanceof MensagemEnterPlayer)
		{
			MensagemEnterPlayer enterPlayer = (MensagemEnterPlayer) mensagem;
			CriarAdversario(enterPlayer.GetId(), new Point(enterPlayer.GetPosicaoX(), enterPlayer.GetPosicaoY()));

			eu.ReportarPosicaoJogador();
		}
		else if (mensagem instanceof MensagemLogout)
		{
			MensagemLogout logout = (MensagemLogout) mensagem;

			RemoverAdversario(logout.GetFrom());
		}
		else if (mensagem instanceof MensagemTiro)
		{
			MensagemTiro mensagemTiro = (MensagemTiro) mensagem;
			Tiro tiro = new Tiro(mensagemTiro, GetContainer());
			_container.AddSprite(tiro);
		}
		else if (mensagem instanceof MensagemChangeEstado)
		{
			MensagemChangeEstado changeEstado = (MensagemChangeEstado) mensagem;

			for(Player player : players)
			{
				if (player.GetId() == changeEstado.GetId())
				{
					player.SetEstadoJogador(changeEstado.GetEstadoMovimentacao());
					player.SetDirecaoAtual(changeEstado.GetEstadoAngulacao());
                                        player.SetVelocidadeY((float)changeEstado.GetVelocidadeY());
                                        if(player.GetVelocidadeY()<0)
                                        {
                                            player.SetPosicao(player.GetPosicao().x, player.GetPosicao().y-2);
                                        }
				}
			}
		}
	}

	/**
	 * Disparado quando uma mensagem de log chega
	 * @owner Victor
	 */
	public void OnMensagemLog(String mensagem)
	{
	}

	/**
	 * Disparado quando o botão de fechar a janela é clicado
	 * @owner Victor
	 */
	private void formWindowClosing(java.awt.event.WindowEvent evt)
	{
		Close();
	}
}
